use crate::db::db_teacher;
use crate::db::entities::teacher::Model;
use crate::rest_api::api_teacher::{CreateParams, SearchParams, Teacher, UpdateParams};

use anyhow::Result;
use anyhow::anyhow;

use rbase::define::{ApiError, BaseError};
use rbase::id_utils::str2id;
use rbase_iam::rest_api::api_user::UserToken;

pub async fn teacher_from_model(teacher_model: &Model) -> Result<Teacher> {
    let teacher = Teacher::from_model(teacher_model);
    Ok(teacher)
}

pub async fn find_teacher_by_id(_user_token: &UserToken, teacher_id: &String) -> Result<Teacher> {
    let teacher_model = db_teacher::find_by_id(str2id(teacher_id)?).await?.ok_or_else(|| anyhow!("Teacher not found"))?;
    Ok(teacher_from_model(&teacher_model).await?)
}

pub async fn delete_teacher_by_id(_user_token: &UserToken, teacher_id: &String) -> Result<()> {
    let teacher_model = db_teacher::find_by_id(str2id(teacher_id)?).await?.ok_or_else(|| anyhow!("Teacher not found"))?;
    db_teacher::delete_by_id(teacher_model.id).await?;
    Ok(())
}

pub async fn update_teacher_by_id(_user_token: &UserToken, teacher_id: &String, new_teacher: &UpdateParams) -> Result<()> {
    let old_model = db_teacher::find_by_id(str2id(teacher_id)?).await?.ok_or_else(|| anyhow!("Teacher not found"))?;
    let new_model = Model {
        id: old_model.id,
        name: Some(new_teacher.name.clone()),
        code: Some(new_teacher.code.clone()),
        ..Default::default()
    };
    db_teacher::update(new_model).await?;
    Ok(())
}

pub async fn create_teacher(_user_token: &UserToken, new_teacher: &CreateParams) -> Result<Teacher> {
    let new_model = Model {
        name: Some(new_teacher.name.clone()),
        code: Some(new_teacher.code.clone()),
        ..Default::default()
    };
    let created_model = db_teacher::insert(new_model).await?;
    Ok(teacher_from_model(&created_model).await?)
}

pub async fn search(_user_token: &UserToken, teacher_search_params: &SearchParams) -> Result<(Vec<Teacher>, i64)> {
    let (teachers, total) = db_teacher::search(&db_teacher::SearchParams {
        name: teacher_search_params.name.clone(),
        offset: teacher_search_params.offset,
        limit: teacher_search_params.limit,
        order_by: teacher_search_params.order_by.clone(),
        order: teacher_search_params.order.clone(),
    })
    .await?;

    let mut teacher_results = Vec::new();
    for teacher in teachers {
        teacher_results.push(teacher_from_model(&teacher).await?);
    }

    Ok((teacher_results, total))
}

pub async fn get_teacher_dashboard_by_id(_user_token: &UserToken, teacher_id: &String) -> Result<()> {
    let _teacher_id = validate_teacher_id(teacher_id).await?;

    Ok(())
}

pub async fn validate_teacher_id(teacher_id_str: &String) -> Result<i64, ApiError> {
    let teacher_id = str2id(teacher_id_str)?;
    match db_teacher::find_by_id(teacher_id).await? {
        Some(_) => return Ok(teacher_id),
        None => {
            return Err(BaseError::ResourceNotFound(teacher_id_str.to_string()).into());
        }
    };
}
